package com.alibaba.task;

import com.alibaba.model.entity.Product;
import com.alibaba.service.ActivityProductService;
import jakarta.annotation.Resource;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.alibaba.constants.OngoingProductSyncConstants.*;

/**
 * 定时任务：同步当前有效且上架的活动商品到 Redis
 * 使用 Redisson 分布式锁保证多实例安全
 * Redis 存储结构：每个商品一个 Hash Key -> ongoing:activity:products:{id}
 * 每个 Hash 字段：name、price、stock、viewCount、status、updateTime
 *
 * - 每 2 分钟执行一次。
 * - 单个商品 Hash 的过期时间为 10 分钟。
 */
@Component
public class OngoingProductSyncTask {

    private static final Logger log = LoggerFactory.getLogger(OngoingProductSyncTask.class);

    @Resource
    private ActivityProductService activityProductService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private RedissonClient redissonClient;

    @Scheduled(cron = "0 */2 * * * ?")
    public void syncOngoingProductsToRedis() {
        RLock lock = redissonClient.getLock(ONGOING_PRODUCT_SYNC_LOCK);
        boolean isLocked = false;
        try {
            // 尝试获取锁，等待1秒，锁自动过期时间5分钟
            isLocked = lock.tryLock(1, 5, TimeUnit.MINUTES);
            if (!isLocked) {
                log.info("[OngoingProductSyncTask] 获取分布式锁失败，跳过本次同步");
                return;
            }

            // 1️⃣ 查询当前有效且上架的活动商品
            List<Product> productList = activityProductService.selectOngoingProduct();
            log.info("[OngoingProductSyncTask] 查询到 {} 条正在进行的活动商品", productList.size());

            // 2️⃣ 查询 Redis 中已有商品 key
            Set<String> keys = redisTemplate.keys(REDIS_KEY_PREFIX + "*");

            // 3️⃣ 数据库无商品，直接删除 Redis 中旧数据
            if (productList.isEmpty()) {
                if (!keys.isEmpty()) {
                    redisTemplate.delete(keys);
                    log.info("[OngoingProductSyncTask] 数据库为空，已删除 Redis 中 {} 个商品 key", keys.size());
                } else {
                    log.info("[OngoingProductSyncTask] 数据库为空，Redis 无数据，无需删除");
                }
                return;
            }

            // 4️⃣ redis中有历史商品数据，先删除旧数据
            if (!keys.isEmpty()) {
                redisTemplate.delete(keys);
                log.info("[OngoingProductSyncTask] 已清理 Redis 中旧商品数据 {} 个", keys.size());
            }

            // 5️⃣ 使用 pipeline 批量写入 Redis
            redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
                for (Product p : productList) {
                    String key = REDIS_KEY_PREFIX + p.getId();
                    byte[] redisKey = redisTemplate.getStringSerializer().serialize(key);

                    Map<byte[], byte[]> hash = new HashMap<>();
                    hash.put(redisTemplate.getStringSerializer().serialize("name"),
                            redisTemplate.getStringSerializer().serialize(p.getName()));
                    hash.put(redisTemplate.getStringSerializer().serialize("price"),
                            redisTemplate.getStringSerializer().serialize(p.getPrice().toString()));
                    hash.put(redisTemplate.getStringSerializer().serialize("stock"),
                            redisTemplate.getStringSerializer().serialize(String.valueOf(p.getStock())));
                    hash.put(redisTemplate.getStringSerializer().serialize("viewCount"),
                            redisTemplate.getStringSerializer().serialize(String.valueOf(p.getViewCount())));
                    hash.put(redisTemplate.getStringSerializer().serialize("status"),
                            redisTemplate.getStringSerializer().serialize(String.valueOf(p.getStatus())));
                    hash.put(redisTemplate.getStringSerializer().serialize("updateTime"),
                            redisTemplate.getStringSerializer().serialize(p.getUpdateTime().toString()));

                    connection.hMSet(redisKey, hash);
                    connection.expire(redisKey, TimeUnit.MINUTES.toSeconds(EXPIRE_MINUTES));
                }
                return null;
            });

            log.info("[OngoingProductSyncTask] 已同步 {} 条商品数据到 Redis（Hash 结构）", productList.size());

        } catch (Exception e) {
            log.error("[OngoingProductSyncTask] 同步异常", e);
        } finally {
            if (isLocked) {
                lock.unlock();
            }
        }
    }
}
